#pragma once
#include <brpc/channel.h>
#include <vector>
#include <unordered_map>
#include <mutex>
#include "logger.hpp"

/*
    功能：封装子服务和etcd && brpc之间的通信连接
    etcd && brpc 二次封装
*/

// 封装单个服务的信道管理类
class ServiceChannel
{  
public:
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
    using ptr = std::shared_ptr<ServiceChannel>;
private:
    std::mutex _mutex;
    int32_t _index;                                     // 轮转下标计数器
    std::string _service_name;                           // 服务名称
    std::vector<ChannelPtr> _channels;                  // 当前服务对应信道集合
    // 服务地址与信道的映射关系
    std::unordered_map<std::string, ChannelPtr> _hosts; 
public:
    ServiceChannel(const std::string &name)
        : _service_name(name),
         _index(0)
    {}
    
    // 服务新上线一个节点，调用append
    void append(const std::string &host,int32_t con_ttl = -1,int timeout_ms = -1,int max_retry = 3)
    {
        auto channel = std::make_shared<brpc::Channel>();
        brpc::ChannelOptions options;
        options.connect_timeout_ms = con_ttl;
        options.timeout_ms = timeout_ms;
        options.max_retry = max_retry;
        options.protocol = "baidu_std";
        //设置信道属性
        int ret = channel->Init(host.c_str(), &options);
        if (ret == -1)
        {
            LOG_ERROR("初始化{}-{}信道失败！", _service_name, host);
            return;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        _hosts.insert(std::make_pair(host, channel));
        _channels.push_back(channel);
        LOG_DEBUG("{}-{}节点新增，添加新节点");
    }
    // 服务下线了一个节点，调用remove
    void remove(const std::string &host)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        //_hosts 作用：仅仅加快查询效率，本质上是一种 空间换时间
        auto it = _hosts.find(host);
        if (it == _hosts.end())
        {
            LOG_WARN("{}-{}节点删除时，未找到节点信息！", _service_name, host);
            return;
        }
        for (auto vit = _channels.begin(); vit != _channels.end(); ++vit)
        {
            if (*vit == it->second)
            {
                _channels.erase(vit);
                LOG_DEBUG("{}-{}节点删除, 进行删除管理！");
                //迭代器失效问题
                break;
            }
        }
        _hosts.erase(it);
    }
    // 通过RR轮转策略，获取一个Channel用于发起对应的RPC调用
    ChannelPtr choose()
    {
        //为什么要加锁？
        std::unique_lock<std::mutex> lock(_mutex);
        if (_channels.size() == 0)
        {
            LOG_ERROR("当前没有能够提供服务的节点");
            return ChannelPtr();
        }
        int32_t idx = _index++ % _channels.size();
        return _channels[idx];
    }
};

// 总体的信道服务管理类
class ServiceManager
{
public:
    using ptr = std::shared_ptr<ServiceManager>;
    // 获取一个指定服务的节点信道
    ServiceChannel::ChannelPtr choose(const std::string &service_name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto sit = _services.find(service_name);
        if (sit == _services.end())
        {
            LOG_ERROR("当前没有能够提供 {} 服务的节点", service_name);
            return ServiceChannel::ChannelPtr();
        }
        return sit->second->choose();
    }
    // 先声明关心哪些服务的上下线，不关心的就不需要管理
    void declared(const std::string &service_name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _follow_service.insert(service_name);
    }
    // 服务上线时调用的回调接口，将服务节点管理
    void onServiceOnline(const std::string &service_instance, const std::string &host)
    {
        std::string service_name = GetServiceName(service_instance);
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);

            auto fit = _follow_service.find(service_name);
            if (fit == _follow_service.end())
            {
                LOG_DEBUG("{}-{} 服务上线，当前并不关心！", service_name, host);
                return;
            }
            // 先获取管理对象，没有则创建，有则添加节点
            auto sit = _services.find(service_name);
            if (sit == _services.end())
            {
                service = std::make_shared<ServiceChannel>(service_name);
                _services.insert(std::make_pair(service_name, service));
            }
            else
            {
                service = sit->second;
            }
        }
        if (!service)
        {
            LOG_ERROR("新增 {} 服务管理节点失败！");
            return;
        }
        service->append(host);
    }
    // 服务下线时调用的回调接口，从服务信道管理中，删除指定节点
    void onServiceOffline(const std::string &service_instance, const std::string &host)
    {
        std::string service_name = GetServiceName(service_instance);
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto fit = _follow_service.find(service_name);
            if (fit == _follow_service.end())
            {
                LOG_DEBUG("{}-{} 服务下线，当前并不关心！", service_name, host);
                return;
            }

            // 先获取管理对象，没有则创建，有则添加节点
            auto sit = _services.find(service_name);
            if (sit == _services.end())
            {
                LOG_WARN("删除 {} 服务节点时，未找到管理对象信息", service_name);
                return;
            }
            else
            {
                service = sit->second;
            }
        }
        service->remove(host);
    }

private:
    //
    std::string GetServiceName(const std::string &service_instance)
    {
        auto pos = service_instance.find_last_of('/');
        if(pos == std::string::npos) return service_instance;
        return service_instance.substr(0, pos);
    }
private:
    std::mutex _mutex;
    std::unordered_set<std::string> _follow_service;
    std::unordered_map<std::string, ServiceChannel::ptr> _services; //string == 服务名称
};